package com.eyalsh.data_structures.linkedLists.basic;

import com.eyalsh.data_structures.linkedLists.DoublyLinkedList;
import com.eyalsh.data_structures.linkedLists.DoublyLinkedListNode;

/**
 * @author Eyal Shalev
 *
 */
public class DoublyELinkedList<T extends DoublyELinkedList<T>> implements DoublyLinkedList<T>, DoublyLinkedListNode<T> {

	private T next, previous;

	private List<T> list;

	private static class List<T> {
		private T head, tail;

		public T head() {
			return head;
		}

		private void head(T value) {
			head = value;
		}

		public T tail() {
			return tail;
		}

		private void tail(T value) {
			tail = value;
		}

		/**
		 * Constructor for ELinkedList.List<br>
		 * 
		 */
		public List(T head) {
			head(head);
			tail(head);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public T data() {
		return (T) this;
	}

	@Override
	public T next() {
		return next;
	}

	protected void next(T value) {
		next = value;
	}

	@Override
	public boolean hasNext() {
		return next() != null;
	}

	@Override
	public boolean hasPrevious() {
		return previous() != null;
	}

	@Override
	public T head() {
		return list.head();
	}

	protected void head(T value) {
		list.head(value);
	}

	@Override
	public boolean empty() {
		return false;
	}

	protected List<T> list() {
		return list;
	}

	protected void list(List<T> value) {
		list = value;
	}

	public T pop() {
		if (hasNext())
			next().next(previous());
		else
			list().tail(previous());
		if (hasPrevious())
			previous().previous(next());
		else
			list().head(next());
		next = previous = null;
		list = new List<T>(data());
		return data();
	}

	public void insertNext(T value) {
		T temp = next();
		next(value.pop());
		value.next(temp);
		value.previous(data());
		value.list(list());
		
		if (value.hasNext())
			value.next().previous(value);
		else
			list().tail(value);
	}

	public void insertPrevious(T value) {
		T temp = previous();
		previous(value.pop());
		value.previous(temp);
		value.next(data());
		value.list(list());
		
		if (value.hasPrevious())
			value.previous().next(value);
		else
			list().head(value);
	}

	public void insertHead(T value) {
		T temp = head();
		list().head(value.pop());
		value.next(temp);
		value.list(list());
	}

	public void insertTail(T value) {
		T temp = tail();
		list().tail(value.pop());
		value.previous(temp);
		value.list(list());
	}

	@Override
	public T previous() {
		return previous;
	}
	
	protected void previous(T value) {
		previous = value;
	}

	@Override
	public T tail() {
		return list().tail();
	}

	
}
